/**
 * DVN: Distributed Virtual Network Simulation Platform
 * Copyright (c) 2006-2009 Tyson Malchow <tyson.malchow@gmail.com>
 *
 * This file is part of the DVN distributed virtual network simulation
 * program.
 *
 * DVN is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * DVN is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with DVN.  If not, see <http://www.gnu.org/licenses/>.
 */

// Maintenance release 0.1, Denis Foo Kune April 22 2010:  network reliability is now properly set from the dsim file.

#include <string.h>

#include "global.h"
#include "sysconfig.h"
#include "sim.h"
#include "routing.h"
#include "nbdf.h"
#include "netconst.h"
#include "rand.h"

sim_master_tp sim_master_create (char * dsim, unsigned int num_slaves) {
	sim_master_tp smaster;
	nbdf_tp start_nb;
	operation_tp op = NULL;

	smaster = malloc(sizeof(*smaster));
	if(!smaster)
		printfault(EXIT_NOMEM, "sim_master_create: Out of memory");

	smaster->num_slaves = num_slaves;
	smaster->num_slaves_complete = 0;
	smaster->net_max_delay = smaster->net_min_delay = 0;
	smaster->end_time_found = 0;

	smaster->dsim = dsim_create(dsim);
	if(!smaster->dsim) {
		free(smaster);
		dlogf(LOG_ERR, "SMaster: Invalid DSIM file submitted.\n");
		return NULL;
	}

	smaster->node_tracking = 	hashtable_create(sysconfig_get_int("sim_nodetrack_hashsize"), sysconfig_get_float("sim_nodetrack_hashgrowth"));
	smaster->network_tracking = hashtable_create(sysconfig_get_int("sim_nettrack_hashsize"), sysconfig_get_float("sim_nettrack_hashgrowth"));
	smaster->module_tracking = 	hashtable_create(sysconfig_get_int("sim_modtrack_hashsize"), sysconfig_get_float("sim_modtrack_hashgrowth"));
	gettimeofday(&smaster->simulation_start,NULL);

	debugf( "SMaster: DSIM validated and loaded OK. Simulation master logic instantiated.\n");

	/* spool all DSIM commands to slaves */
	while((op=dsim_get_nextevent(smaster->dsim, NULL, 1)))
		sim_master_opexec(smaster, op);

	if(!smaster->end_time_found) {
		/* EEEE! hay una problema! */
		dlogf(LOG_ERR, "SMaster: DSIM file submitted has no end time. That would take awhile. Aborting.\n");

		sim_master_destroy(smaster);
		return NULL;
	}

	/* this will cause all worker processes to flip alive and start working! */
	start_nb = nbdf_construct("ii", smaster->net_max_delay, smaster->net_min_delay);
	dvn_packet_route(DVNPACKET_GLOBAL_BCAST, DVNPACKET_LAYER_SIM, 0, SIM_FRAME_START, start_nb);
	nbdf_free(start_nb);

	/* if there are no other slaves, then we've got to send a state frame to the worker(s) so
	 * they don't wait on us.
	 */
	if(num_slaves == 1) {
		nbdf_tp state_frame = nbdf_construct("ittt", 0, PTIME_MAX, PTIME_INVALID, PTIME_MAX);
		dvn_packet_route(DVNPACKET_WORKER_BCAST, DVNPACKET_LAYER_SIM, 0, SIM_FRAME_STATE, state_frame);
		nbdf_free(state_frame);
	}

	debugf( "SMaster: Sent start frame to all slaves.\n");

	return smaster;
}

void sim_free_nodetracker(sim_master_nodetracker_tp nt, int id) {
	if(nt)
		free(nt);
}

void sim_free_nettracker(sim_master_nettracker_tp nt, int id) {
	if(nt)
		free(nt);
}

void sim_free_modtracker(sim_master_modtracker_tp mt, int id) {
	if(mt)
		free(mt);
}


void sim_master_destroy(sim_master_tp sim) {
	if(sim->dsim)
		dsim_destroy(sim->dsim);

	hashtable_walk(sim->network_tracking, (hashtable_walk_callback_tp)&sim_free_nettracker);
	hashtable_destroy(sim->network_tracking);

	hashtable_walk(sim->node_tracking, (hashtable_walk_callback_tp)&sim_free_nodetracker);
	hashtable_destroy(sim->node_tracking);

	hashtable_walk(sim->module_tracking, (hashtable_walk_callback_tp)&sim_free_modtracker);
	hashtable_destroy(sim->module_tracking);

	free(sim);

	debugf( "SMaster: Destroyed.\n");
}


void sim_master_deposit(sim_master_tp smaster, int frametype, nbdf_tp nb) {

	switch(frametype) {
		case SIM_FRAME_DONE_SLAVE:{
			smaster->num_slaves_complete++;
			break;
		}
	}
}

int sim_master_isdone(sim_master_tp smaster) {
	return smaster->num_slaves == smaster->num_slaves_complete ? 1 : 0;
}

void sim_master_opexec(sim_master_tp ma, operation_tp op) {
	switch(op->operation){
		case OP_LOAD_MODULE:
			sim_master_opexec_load_module(ma, op);
			break;

		case OP_CREATE_NETWORK:
			sim_master_opexec_create_network(ma, op);
			break;

		case OP_CONNECT_NETWORK:
			sim_master_opexec_connect_network(ma, op);
			break;

		case OP_DISCONNECT_NETWORK:
			sim_master_opexec_disconnect_network(ma, op);
			break;

		case OP_END_SIM:
			sim_master_opexec_end_sim(ma, op);
			break;

		case OP_CREATE_NODES_VIA:
		case OP_CREATE_NODE_VIA:
		case OP_CREATE_NODE:
			sim_master_opexec_create_nodes(ma, op);
			break;

		default:
			debugf("sim_master_opexec: Unknown op? ");
			break;
	}
}
void sim_master_opexec_load_module(sim_master_tp ma, operation_tp op) {
	sim_master_modtracker_tp modtracker;
	nbdf_tp nb_op;
	simop_t simop;

	debugf("SMaster: DSIM: Module loading: '%s'...\n",op->arguments[0].v.string_val);

	if(op->retval) {
		simop.opcode = SIMOP_CODE_MODLOAD;
		simop.target_time = op->target_time;

		strcpy(simop.detail.modload.file, op->arguments[0].v.string_val);
		while(hashtable_get(ma->module_tracking, (simop.detail.modload.id = dvn_rand_fast(RAND_MAX))));

		/* create the tracker */
		modtracker = malloc(sizeof(*modtracker));
		if(!modtracker)
			printfault(EXIT_NOMEM, "sim_master_opexec_load_module: Out of memory");
		modtracker->id = simop.detail.modload.id;
		hashtable_set(ma->module_tracking, modtracker->id, modtracker);

		/* save it to the variable so DSIM has access to it */
		op->retval->data = modtracker;
		op->retval->data_type = dsim_vartracker_type_modtrack;

		/* encode the simop to NBDF */
		nb_op = simop_nbdf_encode(&simop);

		/* notify all the workers about the module load */
		dvn_packet_route(DVNPACKET_GLOBAL_BCAST, DVNPACKET_LAYER_SIM, 0, SIM_FRAME_OP, nb_op);

		nbdf_free(nb_op);
	}
}

void sim_master_opexec_create_network(sim_master_tp ma, operation_tp op) {
	simop_t simop;
	nbdf_tp nb_op;
	sim_master_nettracker_tp nettrack;
	unsigned int min_delay, max_delay;

	if(op->retval) {
		simop.opcode = SIMOP_CODE_NETWORK;
		simop.target_time = op->target_time;

		nettrack = malloc(sizeof(*nettrack));
		if(!nettrack)
			printfault(EXIT_NOMEM, "sim_master_opexec: OP_CREATE_NETWORK: Out of memory");

		/* get a unique random ID */
		while(hashtable_get(ma->network_tracking, (nettrack->id = dvn_rand_fast(RAND_MAX))));
		hashtable_set(ma->network_tracking, nettrack->id, nettrack);
		op->retval->data = nettrack;
		op->retval->data_type = dsim_vartracker_type_nettrack;

		debugf("SMaster: DSIM: Creating a network '%i' inside %s\n",nettrack->id,op->retval->varname);

		simop.detail.network.method = SIMOP_NETWORK_CREATE;
		simop.detail.network.id2 = 0;
		simop.detail.network.id1 = nettrack->id;
		simop.detail.network.base_delay = (int) op->arguments[0].v.double_val;
		simop.detail.network.width = (int) op->arguments[1].v.double_val;
		simop.detail.network.tail_width = (int) op->arguments[2].v.double_val;
		simop.detail.network.reliability = (int) (op->arguments[3].v.double_val * VCI_RLBLTY_FAC);

		min_delay = simop.detail.network.base_delay - simop.detail.network.width;
		max_delay = simop.detail.network.base_delay + simop.detail.network.width + simop.detail.network.tail_width;

		/* compute min/max network delays */
		if(ma->net_min_delay == 0 || ma->net_min_delay > min_delay)
			ma->net_min_delay = min_delay;

		if(ma->net_max_delay == 0 || ma->net_max_delay < max_delay)
			ma->net_max_delay = max_delay;

		/* encode the simop to NBDF */
		nb_op = simop_nbdf_encode(&simop);

		/* notify all the workers about the module load */
		dvn_packet_route(DVNPACKET_GLOBAL_BCAST, DVNPACKET_LAYER_SIM, 0, SIM_FRAME_OP, nb_op);

		nbdf_free(nb_op);
	}
}

void sim_master_opexec_connect_network(sim_master_tp ma, operation_tp op) {
	simop_t simop;
	nbdf_tp nb_op;
	unsigned int min_delay, max_delay;

	simop.opcode = SIMOP_CODE_NETWORK;
	simop.target_time = op->target_time;

	if(op->arguments[0].v.var_val && op->arguments[1].v.var_val &&
			op->arguments[0].v.var_val->data_type == dsim_vartracker_type_nettrack &&
			op->arguments[1].v.var_val->data_type == dsim_vartracker_type_nettrack &&
			op->arguments[0].v.var_val->data && op->arguments[1].v.var_val->data) {

		simop.detail.network.method = SIMOP_NETWORK_CONNECT;
		simop.detail.network.id1 = ((sim_master_nettracker_tp)op->arguments[0].v.var_val->data)->id;
		simop.detail.network.id2 = ((sim_master_nettracker_tp)op->arguments[1].v.var_val->data)->id;

		simop.detail.network.base_delay = (int) op->arguments[2].v.double_val;
		simop.detail.network.width = (int) op->arguments[3].v.double_val;
		simop.detail.network.tail_width = (int) op->arguments[4].v.double_val;
		simop.detail.network.reliability = VCI_RLBLTY_100;

		min_delay = simop.detail.network.base_delay - simop.detail.network.width;
		max_delay = simop.detail.network.base_delay + simop.detail.network.width + simop.detail.network.tail_width;

		/* compute min/max network delays */
		if(ma->net_min_delay == 0 || ma->net_min_delay > min_delay)
			ma->net_min_delay = min_delay;

		if(ma->net_max_delay == 0 || ma->net_max_delay < max_delay)
			ma->net_max_delay = max_delay;

		/* encode the simop to NBDF */
		nb_op = simop_nbdf_encode(&simop);

		/* notify all the workers about the module load */
		dvn_packet_route(DVNPACKET_GLOBAL_BCAST, DVNPACKET_LAYER_SIM, 0, SIM_FRAME_OP, nb_op);


		nbdf_free(nb_op);

		debugf( "SMaster: DSIM: Connecting networks %i and %i\n",((sim_master_nettracker_tp)op->arguments[0].v.var_val->data)->id,
				((sim_master_nettracker_tp)op->arguments[1].v.var_val->data)->id);
	}
}

void sim_master_opexec_disconnect_network(sim_master_tp ma, operation_tp op) {
	simop_t simop;
	nbdf_tp nb_op;

	simop.opcode = SIMOP_CODE_NETWORK;
	simop.target_time = op->target_time;

	if(op->arguments[0].v.var_val->data && op->arguments[1].v.var_val->data_type == dsim_vartracker_type_nettrack && op->arguments[1].v.var_val->data) {
		simop.detail.network.method = SIMOP_NETWORK_DISCONNECT;
		simop.detail.network.id1 = ((sim_master_nettracker_tp)op->arguments[0].v.var_val->data)->id;
		simop.detail.network.id2 = ((sim_master_nettracker_tp)op->arguments[1].v.var_val->data)->id;

		/* encode the simop to NBDF */
		nb_op = simop_nbdf_encode(&simop);

		/* notify all the workers about the module load */
		dvn_packet_route(DVNPACKET_GLOBAL_BCAST, DVNPACKET_LAYER_SIM, 0, SIM_FRAME_OP, nb_op);

		nbdf_free(nb_op);

		debugf( "SMaster: DSIM: Disconnecting networks %i and %i\n",((sim_master_nettracker_tp)op->arguments[0].v.var_val->data)->id,
				((sim_master_nettracker_tp)op->arguments[1].v.var_val->data)->id);
	}
}

void sim_master_opexec_end_sim(sim_master_tp ma, operation_tp op) {
	nbdf_tp nb_op;
	simop_t simop;

	simop.opcode = SIMOP_CODE_END;
	simop.target_time = op->target_time;
	nb_op = simop_nbdf_encode(&simop);

	dvn_packet_route(DVNPACKET_GLOBAL_BCAST, DVNPACKET_LAYER_SIM, 0, SIM_FRAME_OP, nb_op);

	nbdf_free(nb_op);

	debugf("SMaster: DSIM: End of simulation\n");

	ma->end_time_found = 1;
}

void sim_master_opexec_create_nodes(sim_master_tp ma, operation_tp op) {
	static unsigned int slave_turn = 0;
	simop_t simop;
	nbdf_tp nb_op;

	sim_master_nodetracker_tp bootstrap = NULL;
	sim_master_modtracker_tp modtracker = NULL;
	sim_master_nettracker_tp nettracker = NULL;
	unsigned int quantity = 1;

	simop.opcode = SIMOP_CODE_CNODES;
	simop.target_time = op->target_time;

	simop.detail.cnodes.logging_level = 0;
	simop.detail.cnodes.logging_channel = 0;
	simop.detail.cnodes.flags = 0;
	simop.detail.cnodes.tracker_id = 0;
	simop.detail.cnodes.bootstrap_id = 0;

	debugf ("SMaster: DSIM: Spawning nodes, at: %d\n", op->target_time);

	/* extract arguments */
	if(op->operation == OP_CREATE_NODES_VIA) {

		// validate variable types...
		if(op->arguments[1].v.var_val->data_type != dsim_vartracker_type_modtrack ||
				op->arguments[2].v.var_val->data_type != dsim_vartracker_type_nettrack ||
				op->arguments[3].v.var_val->data_type != dsim_vartracker_type_nodetrack) {

			dlogf(LOG_ERR, "SMaster: DSIM: Invalid variable types supplied to create_nodes_via\n");
			return ;
		}

		quantity = (unsigned int)op->arguments[0].v.double_val;
		modtracker = op->arguments[1].v.var_val->data;
		nettracker = op->arguments[2].v.var_val->data;
		bootstrap = op->arguments[3].v.var_val->data;

		simop.detail.cnodes.logging_channel = (unsigned char)op->arguments[4].v.double_val;
		simop.detail.cnodes.logging_level = (int)op->arguments[5].v.double_val;

		strncpy(simop.detail.cnodes.cl_args, op->arguments[6].v.string_val, SIMOP_CNODES_CLARGS_LEN);

		simop.detail.cnodes.bootstrap_id = bootstrap->id;
		simop.detail.cnodes.flags |= SIMOP_CNODES_BOOTSTRAP;

	} else if(op->operation == OP_CREATE_NODE_VIA) {
		// validate variable types...
		if(op->arguments[0].v.var_val->data_type != dsim_vartracker_type_modtrack ||
				op->arguments[1].v.var_val->data_type != dsim_vartracker_type_nettrack ||
				op->arguments[2].v.var_val->data_type != dsim_vartracker_type_nodetrack) {

			dlogf(LOG_ERR, "SMaster: DSIM: Invalid variable types supplied to create_node_via\n");
			return ;
		}

		modtracker = op->arguments[0].v.var_val->data;
		nettracker = op->arguments[1].v.var_val->data;
		bootstrap = op->arguments[2].v.var_val->data;

		simop.detail.cnodes.logging_channel = (unsigned char)op->arguments[3].v.double_val;
		simop.detail.cnodes.logging_level = (int)op->arguments[4].v.double_val;

		strncpy(simop.detail.cnodes.cl_args, op->arguments[5].v.string_val, SIMOP_CNODES_CLARGS_LEN);

		simop.detail.cnodes.bootstrap_id = bootstrap->id;
		simop.detail.cnodes.flags |= SIMOP_CNODES_BOOTSTRAP;
	} else {
		// validate variable types...
		if(op->arguments[0].v.var_val->data_type != dsim_vartracker_type_modtrack ||
				op->arguments[1].v.var_val->data_type != dsim_vartracker_type_nettrack) {

			dlogf(LOG_ERR, "SMaster: DSIM: Invalid variable types supplied to create_node\n");
			return ;
		}

		modtracker = op->arguments[0].v.var_val->data;
		nettracker = op->arguments[1].v.var_val->data;

		simop.detail.cnodes.logging_channel = (char)op->arguments[2].v.double_val;
		simop.detail.cnodes.logging_level = (int)op->arguments[3].v.double_val;
		strncpy(simop.detail.cnodes.cl_args, op->arguments[4].v.string_val, SIMOP_CNODES_CLARGS_LEN);
	}

	simop.detail.cnodes.network_id = nettracker->id;
	simop.detail.cnodes.module_id = modtracker->id;

	/* if this node is supposed to be saved to a variable, we have to turn on node tracking
	 * to get the IP assigned by the worker node responsible */
	if(op->retval && op->operation != OP_CREATE_NODES_VIA) {
		sim_master_nodetracker_tp tracker = malloc(sizeof(*tracker));

		if(!tracker)
			printfault(EXIT_NOMEM, "Out of memory: sim_master_opexec_create_nodes");

		while(hashtable_get(ma->node_tracking, (tracker->id = dvn_rand_fast(RAND_MAX))));
		hashtable_set(ma->node_tracking, tracker->id, tracker);

		simop.detail.cnodes.tracker_id = tracker->id;
		simop.detail.cnodes.flags |= SIMOP_CNODES_TRACKING;

		op->retval->data = tracker;
		op->retval->data_type = dsim_vartracker_type_nodetrack;
	}

	if(op->operation==OP_CREATE_NODE||op->operation==OP_CREATE_NODE_VIA) {
		simop.detail.cnodes.quantity = 1;
		nb_op = simop_nbdf_encode(&simop);
		dvn_packet_route(DVNPACKET_SLAVE, DVNPACKET_LAYER_SIM, (slave_turn++) % ma->num_slaves, SIM_FRAME_OP, nb_op);
		nbdf_free(nb_op);

	} else {
		nbdf_tp nb_op2; int i;
		int remain = 0;

		simop.detail.cnodes.quantity = quantity/ma->num_slaves;
		nb_op = simop_nbdf_encode(&simop);

		remain = quantity % ma->num_slaves;
		if(remain > ma->num_slaves) {
			int extra_dist = remain / ma->num_slaves;
			remain %= ma->num_slaves;
			simop.detail.cnodes.quantity += extra_dist;
		}

		simop.detail.cnodes.quantity += remain;
		nb_op2 = simop_nbdf_encode(&simop);

		/* multi node creation. split the job up. */
		for(i=0; i<ma->num_slaves; i++) {
			if(i==(slave_turn % ma->num_slaves))
				dvn_packet_route(DVNPACKET_SLAVE, DVNPACKET_LAYER_SIM, i, SIM_FRAME_OP, nb_op2);
			else
				dvn_packet_route(DVNPACKET_SLAVE, DVNPACKET_LAYER_SIM, i, SIM_FRAME_OP, nb_op);
		}

		nbdf_free(nb_op2);
		nbdf_free(nb_op);

		slave_turn++;
	}
}







//sim_master_tp sim_master_create (char * dsim, unsigned int num_workers) {
//	sim_master_tp sim;
//	int i;
//
//	sim = malloc(sizeof(*sim));
//	sim->num_workers = num_workers;
//	sim->state = SIM_MASTATE_SPOOLING;
//	sim->worker_states = malloc(sizeof(*sim->worker_states) * sim->num_workers);
//
//	if(!sim->worker_states)
//		printfault(EXIT_NOMEM, "sim_master_create: Out of memory");
//
//
//	sim->net_min_delay = 0;
//	sim->net_max_delay = 0;
//
//	sim->sim_end_time = 10000;
//
//	sim->num_modules = sim->num_networks = 0;
//	sim->num_worker_complete = 0;
//
//	sim->node_tracking = 	hashtable_create(sysconfig_get_int("sim_nodetrack_hashsize"),sysconfig_get_float("sim_nodetrack_hashgrowth"));
//	sim->network_tracking = hashtable_create(sysconfig_get_int("sim_nettrack_hashsize"),sysconfig_get_float("sim_nettrack_hashgrowth"));
//	sim->module_tracking = 	hashtable_create(sysconfig_get_int("sim_modtrack_hashsize"), sysconfig_get_int("sim_modtrack_hashgrowth"));
//	gettimeofday(&sim->simulation_start,NULL);
//
//	dlogf(LOG_INFO, "Simulation master creation. DSIM file submitted.\n");
//
//	sim->dsim = dsim_create(dsim);
//	if(!sim->dsim) {
//		dlogf(LOG_ERR, "Invalid DSIM file submitted.\n");
//		sim_master_destroy( sim );
//		return NULL;
//	}
//
//	return sim;
//}
//
//int sim_master_isdone (sim_master_tp ma) {
//	return (ma->state == SIM_MASTATE_DONE) ? 1 : 0 ;
//}
//
//void dvn_tell_worker (unsigned int, nbdf_tp);
//void sim_master_wbroadcast(sim_master_tp ma, int command, nbdf_tp nb) {
//	int i;
//	nbdf_tp framenb = nbdf_construct("in", command, nb);
//
//	for(i=0; i<ma->num_workers; i++)
//		dvn_tell_worker(i, framenb);
//
//	nbdf_free(framenb);
//}
//
///* simulation core, master */
//int sim_master_process (sim_master_tp ma){
//	ptime_t pt;
//	int rv = 1;
//	nbdf_tp nb;
//
//	if(!ma)
//		return 0;
//
//	/* objectives: 	-process all DSIM commands for upcoming time-cycle. (includes sending out DSIM children ops to workers)
//	 * 				-send a time/start-working notification to all workers.
//	 * 				-each worker should report back when finished, along with their next occuring event time.
//	 * 				-get min_time(reported, dsim events). repeat.
//	 */
//	switch( ma->state ) {
//		case SIM_MASTATE_SPOOLING: {
//			operation_tp op = NULL;
//
//			/* spool all dsim commands. */
//			while((op=dsim_get_nextevent(ma->dsim,&pt,1))) {
//				sim_master_opexec(ma, op);
//				dsim_destroy_operation(op);
//				op = NULL;
//			}
//
//			nb = nbdf_construct("tt", ma->net_min_delay, ma->net_max_delay);
//			sim_master_wbroadcast(ma,SIM_WFRAME_CMD_STARTSIM, nb);
//			nbdf_free(nb);
//
//			ma->state = SIM_MASTATE_WPROC;
//			rv = 0;
//
//			break;
//		}
//
//		case SIM_MASTATE_WPROC:
//			/*waiting for all workers to report complete w/a next_time.
//			therefore, don't do a thing.*/
//			rv = 0;
//			break;
//
//
//		case SIM_MASTATE_DONE:
//			rv = -1;
//			break;
//
//		case SIM_MASTATE_ENDING:
//			/* waiting for all workers to report complete ... */
//			rv = 0;
//			break;
//	}
//
//	/*getchar();*/
//#ifdef DEBUG_CYCLE_GETCHAR
//	printf("-- cycle --\n");
//	getchar();
//#endif
//
//	return rv;
//}
//
//

//
//

//}
//
//void sim_master_deposit (sim_master_tp ma, int source_worker_id, nbdf_tp nb){
//	int command;
//	nbdf_tp sim_nb;
//
//	if(!ma)
//		return;
//
//	nbdf_read(nb, "in", &command, &sim_nb);
//
//	if(command == SIM_MFRAME_CMD_COMPLETE) {
//		debugf( "Worker %d reported complete with sim.\n", source_worker_id);
//		ma->num_worker_complete++;
//
//		if(ma->num_worker_complete == ma->num_workers) {
//			struct timeval total_time;
//			gettimeofday(&total_time,NULL);
//
//			total_time.tv_sec -= ma->simulation_start.tv_sec;
//			if(total_time.tv_usec < ma->simulation_start.tv_usec) {
//				total_time.tv_sec--;
//				total_time.tv_usec += 1000000;
//			}
//			total_time.tv_usec -= ma->simulation_start.tv_usec;
//
//			dlogf(LOG_INFO,"Master: Simulation complete. All workers idle. total time: %d seconds, %d useconds.\n", total_time.tv_sec, total_time.tv_usec);
//
//			ma->state = SIM_MASTATE_DONE;
//		}
//	} else if(command == SIM_MFRAME_CMD_ENDERROR) {
//		char * error;
//
//		nbdf_read(sim_nb, "S", &error);
//		dlogf(LOG_ERR, "Master: Simulation has ended because of a worker error: %s\n", error);
//
//		free(error);
//		ma->state = SIM_MASTATE_DONE;
//
//	} /*else if(command == SIM_MFRAME_CMD_TRACKRESP) {
//		sim_nodetracker_tp nt;
//		int tracking_id;
//		in_addr_t address;
//
//		nbdf_read(sim_nb, "ia", &tracking_id, &address);
//
//		nt = hashtable_get(ma->node_tracking, tracking_id);
//		if(nt) {
//			debugf( "Master: Acknowledged IP tracking response id %i for address %i.\n", tracking_id, address);
//			nt->addr = address;
//		}
//	}*/
//
//	nbdf_free(sim_nb);
//
//	return;
//}
//
//
//void dvn_tell_master (nbdf_tp);
//void sim_tell_master(int command, nbdf_tp nb) {
//	nbdf_tp sim_nb;
//
//	sim_nb = nbdf_construct("in", command, nb);
//	dvn_tell_master(sim_nb);
//	nbdf_free(sim_nb);
//
//	return;
//}
//

